;; (Programming against the v1.38 interface of docker engine.)

(use-modules (web client)
             (web uri)
             (json)
             (ice-9 iconv))

(define* (connect-to-docker-socket #:key (socket-path "/var/run/docker.sock"))
  (let ([docker-sock-addr (make-socket-address AF_UNIX socket-path)]
        [docker-sock (socket PF_UNIX SOCK_STREAM 0)])
    ;; socket options:
    ;; https://www.gnu.org/software/libc/manual/html_node/Socket_002dLevel-Options.html
    (setsockopt docker-sock SOL_SOCKET SO_REUSEADDR 1)
    ;; usage of connect:
    ;; https://www.gnu.org/software/guile/manual/html_node/Network-Sockets-and-Communication.html#Network-Sockets-and-Communication
    ;; server side would use `bind`, `accept` and `listen`.
    ;; client side uses `connect` and `close`.
    (connect docker-sock docker-sock-addr)
    docker-sock))


(define (conditionally-make-alist-of names vals)
  (cond [(null? names) '()]
        [(car vals)
         (cons (cons (car names) (car vals))
               (conditionally-make-alist-of (cdr names) (cdr vals)))]
        [else (conditionally-make-alist-of (cdr names) (cdr vals))]))

(define (alist-to-query-params alist)
  (string-join (map (lambda (assoc)
                      (string-append (car assoc) "=" (cdr assoc)))
                    alist)
               "&"))

(define (make-complete-api-url api-route query-params-as-string)
  (string-append api-route "?" query-params-as-string))

(define (scm-json->uri-encoded-string scm-json)
  "The argument scm-json is the Scheme representation of guile-json
for JSON."
  (uri-encode (scm->json-string scm-json)))

(define* (/containers/json dock-sock #:key (limit #f) (filters #f) (all #f) (size #f))
  (define (build-api-url)
    (display
       (simple-format #f
                      "URL-ENCODED FILTERS: ~s\n"
                      (scm-json->uri-encoded-string filters)))
    (let* ([filters-url-encoded
            (scm-json->uri-encoded-string filters)]
           [query-params
            (alist-to-query-params
             (conditionally-make-alist-of
              '("limit" "filters" "all" "size")
              (list limit filters-url-encoded all size)))])
      (string-append "/containers/json" "?" query-params)))

  (call-with-values
      (lambda ()
        (http-get (build-api-url)
                  #:port dock-sock
                  ;; dockerd uses HTTP 1.1 it seems.
                  ;; other values will result in: "Bad Request: unsupported protocol version"
                  #:version '(1 . 1)
                  #:keep-alive? #f
                  ;; Apparently the `host` header must be specified.
                  ;; The `host` header in this case is ("localhost" . #f).
                  ;; The `host` header contains domain and port
                  #:headers '((host . ("localhost" . #f))
                              #;(content-type . "application/x-www-form-urlencoded"))
                  #:decode-body? #t
                  #:streaming? #f))
    (lambda (response response-text)
      (display
       (simple-format #f
                      "RESPONSE TEXT: ~s"
                      (bytevector->string response-text "utf-8")))
      (let ([resp-text-as-string (bytevector->string response-text "utf-8")])
        (cons response resp-text-as-string)))))

(let ([dock-sock (connect-to-docker-socket)])
  (let ([resp-and-resp-text
         (/containers/json dock-sock
                           #:all "true"
                           #:filters '(("name" . #("db"))
                                       ("status" . #("running" "exited"))))])
    (display resp-and-resp-text)
    (newline)))
